<template>
  <div style="width:100%; height:800px">
    <div id="container"></div>
  </div>
</template>

<script>
import * as THREE from './build/three.module.js'
import { OrbitControls } from './jsm/controls/OrbitControls.js' // 轨道控制
import { GUI } from './jsm/libs/dat.gui.module.js'
// import { OrbitControls } from "./jsm/controls/OrbitControls.js"; // 轨道控制
import { EffectComposer } from './jsm/postprocessing/EffectComposer.js' // 效果合成器--EffectComposer依赖RenderPass.js、ShaderPass.js、CopyShader.js库
import { UnrealBloomPass } from './jsm/postprocessing/UnrealBloomPass.js' // 虚幻效果通道
import { RenderPass } from './jsm/postprocessing/RenderPass.js' // 渲染通道
import { ShaderPass } from './jsm/postprocessing/ShaderPass.js' // 着色器通道
import { FXAAShader } from './jsm/shaders/FXAAShader.js' // 抗锯齿着色器
let scene, camera, renderer, bloomComposer, finalComposer
const ENTIRE_SCENE = 0, // 全部的，整个的场景
  BLOOM_SCENE = 1 // 光晕场景
const bloomLayer = new THREE.Layers() // 光晕层次-创建一个图层对象
bloomLayer.set(BLOOM_SCENE) // 先把光晕层次设置光晕场景的层次1
const darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' }) // 跟辉光光晕有关的变量
const materials = {} // 跟辉光光晕有关的变量
const params = {
  exposure: 0, // 暴露
  bloomStrength: 1.5, // 光晕强度
  bloomThreshold: 0, // 光晕阈值
  bloomRadius: 0, // 光晕半径
}
const init = () => {
  var container = document.getElementById('container')
  // 场景
  scene = new THREE.Scene()
  // 相机
  camera = new THREE.PerspectiveCamera(
    70,
    window.innerWidth / window.innerHeight,
    1,
    100000
  )
  camera.position.set(1500, 2000, 500)
  camera.position.y = 50
  // 渲染器
  renderer = new THREE.WebGLRenderer({
    antialias: true, // 抗锯齿
  })
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.setSize(window.innerWidth, window.innerHeight)
  renderer.toneMapping = THREE.ReinhardToneMapping
  // document.body.appendChild(renderer.domElement)
  container.appendChild(renderer.domElement)
  // 环境光
  const light = new THREE.AmbientLight(0xffffff, 0.6)
  light.layers.enable(0)
  light.layers.enable(1)
  scene.add(light)
  // 控制器
  const controls = new OrbitControls(camera, renderer.domElement)
  scene.add(new THREE.AxesHelper(100))
  window.onresize = () => {
    renderer.setSize(window.innerWidth, window.innerHeight)
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
  }
}
let bloomPass
const initComposer = () => {
  // 去掉锯齿---1
  // 通过ShaderPass构造函数把FXAAShader着色器和uniforms构成的对象作为参数，创建一个锯齿通道FXAAShaderPass,然后把锯齿通道插入到composer中。
  const effectFXAA = new ShaderPass(FXAAShader)
  effectFXAA.uniforms['resolution'].value.set(
    0.6 / window.innerWidth,
    0.6 / window.innerHeight
  ) // 渲染区域Canvas画布宽高度  不一定是全屏，也可以是区域值
  effectFXAA.renderToScreen = true
  // 去掉锯齿---1
  const renderScene = new RenderPass(scene, camera) // RenderPass这个通道会在当前场景（scene）和摄像机（camera）的基础上渲染出一个新场景，新建：
  // 添加光晕效果---2
  bloomPass = new UnrealBloomPass( // UnrealBloomPass通道可实现一个泛光效果。
    new THREE.Vector2(window.innerWidth, window.innerHeight),
    1.5,
    0.4,
    0.85
  )
  bloomPass.threshold = params.bloomThreshold
  bloomPass.strength = params.bloomStrength
  bloomPass.radius = params.bloomRadius
  // 添加光晕效果---2
  // 着色器通道容器--放进容器里
  bloomComposer = new EffectComposer(renderer) // EffectComposer可以理解为着色器通道容器，着色器通道按照先后顺序添加进来并执行
  bloomComposer.renderToScreen = false
  bloomComposer.addPass(renderScene)
  bloomComposer.addPass(bloomPass) // 添加光晕效果
  bloomComposer.addPass(effectFXAA) // 去掉锯齿
  // 着色器通道容器--放进容器里
  const finalPass = new ShaderPass(
    new THREE.ShaderMaterial({
      uniforms: {
        baseTexture: { value: null },
        bloomTexture: { value: bloomComposer.renderTarget2.texture },
      },
      vertexShader: `
           varying vec2 vUv;
      void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      }
          `,
      fragmentShader: `
          uniform sampler2D baseTexture;
      uniform sampler2D bloomTexture;
      varying vec2 vUv;
      void main() {
          gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
      }
          `,
      defines: {},
    }),
    'baseTexture'
  )
  finalPass.needsSwap = true
  finalComposer = new EffectComposer(renderer)
  finalComposer.addPass(renderScene)
  finalComposer.addPass(finalPass)
  finalComposer.addPass(effectFXAA)
}
const render1 = () => {
  scene.traverse((obj) => {
    if (obj.isMesh && bloomLayer.test(obj.layers) === false) {
      materials[obj.uuid] = obj.material
      obj.material = darkMaterial
    }
  })
  bloomComposer.render()
  scene.traverse((obj) => {
    if (materials[obj.uuid]) {
      obj.material = materials[obj.uuid]
      delete materials[obj.uuid]
    }
  })
  finalComposer.render()
  requestAnimationFrame(render)
}
const render = () => {
  scene.traverse(darkenNonBloomed) // 隐藏不需要辉光的物体
  bloomComposer.render()
  scene.traverse(restoreMaterial) // 还原
  finalComposer.render()
  requestAnimationFrame(render)
}
const bloomIgnore = [] // 跟辉光光晕有关的变量
function darkenNonBloomed(obj) {
  if (obj instanceof THREE.Scene) {
    // 此处忽略Scene，否则场景背景会被影响
    materials.scene = obj.background
    obj.background = null
    return
  }
  if (
    obj instanceof THREE.Sprite || // 此处忽略Sprite
    bloomIgnore.includes(obj.type) ||
    (obj.isMesh && bloomLayer.test(obj.layers) === false) // 判断与辉光是否同层
  ) {
    materials[obj.uuid] = obj.material
    obj.material = darkMaterial
  }
}
function restoreMaterial(obj) {
  if (obj instanceof THREE.Scene) {
    obj.background = materials.scene
    delete materials.scene
    return
  }
  if (materials[obj.uuid]) {
    obj.material = materials[obj.uuid]
    delete materials[obj.uuid]
  }
}
const main = () => {
  init()
  initComposer()
  initModel()
  changeBloom()
}

function initModel() {
  let mat = new THREE.MeshBasicMaterial({
    color: 'red',
  })
  let geo = new THREE.BoxGeometry(200, 200, 200)
  let mesh = new THREE.Mesh(geo, mat)
  mesh.position.set(500, 0, 0)
  mesh.layers.enable(0) // 分层
  scene.add(mesh)

  let mat1 = new THREE.MeshBasicMaterial({
    color: 'green',
  })
  let geo1 = new THREE.BoxGeometry(200, 200, 200)
  let mesh02 = new THREE.Mesh(geo1, mat1)
  mesh02.position.set(-500, 0, 0)
  mesh02.layers.enable(1) // 分层
  scene.add(mesh02)
}
let gui = null
function changeBloom() {
  if (gui) return
  gui = new GUI()
  gui.add(params, 'exposure', 0.1, 2).onChange(function(value) {
    renderer.toneMappingExposure = Math.pow(value, 4.0)
  })
  gui.add(params, 'bloomThreshold', 0.0, 1.0).onChange(function(value) {
    bloomPass.threshold = Number(value)
  })
  gui.add(params, 'bloomStrength', 0.0, 3.0).onChange(function(value) {
    bloomPass.strength = Number(value)
  })
  gui
    .add(params, 'bloomRadius', 0.0, 1.0)
    .step(0.01)
    .onChange(function(value) {
      bloomPass.radius = Number(value)
    })
}

export default {
  data() {
    return {}
  },
  mounted() {
    main()
    render()
  },
  methods: {},
}
</script>
